﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using SmlEngine;
using SmlEngine.Graphics;
using SmlEngine.Sprites;
using SmlEngine.Sprites.Collections;

namespace SmlEngine.Sprites.Base.WorldMap
{
    public class OverworldBorder : IGameObject
    {
        #region Properties and Fields

        private const string contentFolder = @"world/border/";
        private Vector2 screenSize;
        private Vector2 screenTileSize
        {
            get { return (screenSize / new Vector2(8, 8)).Floor(); }
        }
        private Random random;

        /* IGameObject Properties */
        public Vector2 Position { get; set; }
        public Vector2 Size { get { return screenSize; } set { throw new NotImplementedException(); } }
        public Rectangle Area
        {
            get
            {
                return Position.ToRectangle(Size);
            }
        }
        public Visibility Visibility
        {
            get
            {
                return Sprites.Visibility.Visible;
            }
            set { throw new NotImplementedException(); }
        }

        /* Textures and Resources*/
        private Texture2D finalBorder;
        private Texture2D coinSprite; // TODO: get graphics
        private Texture2D livesSprite; // TODO: get graphics
        private BitmapFontHandler font;

        /* Object positions */
        private Vector2 levelTextPos;
        private Vector2 coinSpritePos;
        private Vector2 coinTextPos;
        private Vector2 livesSpritePos;
        private Vector2 livesTextPos;

        /* Text Fields */
        public string LevelText;
        public string CoinCount;
        public string LivesText;

        /* Border construction */
        private string[] randomTileNames = new string[] {"flower", "spade", "star" };
        private Vector2 tileSize = new Vector2(8, 8);
        private Dictionary<Vector2, string> borderTiles; // key: position, string: content name of tile
        private Dictionary<Vector2, Texture2D> borderTileTextures; // key: position, Texture2D: texture

        #endregion

        #region Constructors and Content Handling

        public OverworldBorder(Vector2 _screenSize)
        {
            if (_screenSize.X <= 48 || _screenSize.Y <= 48)
            {
                throw new ArgumentException("Could not construct the world border; the screen is too small. [SmlEngine.Sprites.Base.WorldMap.OverworldBorder.ctor(Vector2)]");
            }
            screenSize = _screenSize;
            random = new Random(DateTime.Now.Millisecond * DateTime.Now.Second); // range: 0 to 60000, should be random enough
            ConstructSMWBorder();
        }

        public void LoadContent(ContentManager Content, GraphicsDevice Graphics)
        {
            borderTileTextures = new Dictionary<Vector2, Texture2D>();
            Dictionary<string, Texture2D> borderTextures = new Dictionary<string, Texture2D>();
            borderTextures.Add("tile", Content.Load<Texture2D>(contentFolder + "tile"));
            borderTextures.Add("flower", Content.Load<Texture2D>(contentFolder + "flower"));
            borderTextures.Add("star", Content.Load<Texture2D>(contentFolder + "star"));
            borderTextures.Add("spade", Content.Load<Texture2D>(contentFolder + "spade"));
            borderTextures.Add("edge_topleft", Content.Load<Texture2D>(contentFolder + "edge_topleft"));
            borderTextures.Add("edge_top", Content.Load<Texture2D>(contentFolder + "edge_top"));
            borderTextures.Add("edge_topright", Content.Load<Texture2D>(contentFolder + "edge_topright"));
            borderTextures.Add("edge_right", Content.Load<Texture2D>(contentFolder + "edge_right"));
            borderTextures.Add("edge_bottomright", Content.Load<Texture2D>(contentFolder + "edge_bottomright"));
            borderTextures.Add("edge_bottom", Content.Load<Texture2D>(contentFolder + "edge_bottom"));
            borderTextures.Add("edge_bottomleft", Content.Load<Texture2D>(contentFolder + "edge_bottomleft"));
            borderTextures.Add("edge_left", Content.Load<Texture2D>(contentFolder + "edge_left"));
            borderTextures.Add("shadow_bottom", Content.Load<Texture2D>(contentFolder + "shadow_bottom"));
            borderTextures.Add("shadow_bottomleft", Content.Load<Texture2D>(contentFolder + "shadow_bottomleft"));
            borderTextures.Add("shadow_bottomright", Content.Load<Texture2D>(contentFolder + "shadow_bottomright"));
            borderTextures.Add("shadow_right", Content.Load<Texture2D>(contentFolder + "shadow_right"));
            borderTextures.Add("shadow_topright", Content.Load<Texture2D>(contentFolder + "shadow_topright"));

            LoadFont(Content);
            
            // space reserved for font, coin, and lives content

            if (!borderTiles.Any())
            {
                ConstructSMWBorder();
            }

            // Construct the textures from the tile name list
            foreach (var tileDefiniton in borderTiles)
            {
                borderTileTextures.Add(tileDefiniton.Key, borderTextures[tileDefiniton.Value]);
            }

            finalBorder = GraphicsHelper.Merge(Graphics, borderTileTextures, (int)screenSize.Y, (int)screenSize.X);

            //borderTileTextures.Clear();
        }

        public void UnloadContent() { }

        public void Initialize() { }
        #endregion

        #region Update and Draw

        public void Update(GameTime gametime) { }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(finalBorder, Vector2.Zero, Color.White);
            font.Draw(spriteBatch, LevelText, new Vector2(112, 24));
            font.Draw(spriteBatch, LivesText, new Vector2(80, 24));
        }

        #endregion

        #region Border Construction
        private void ConstructSMWBorder()
        {
            /* Constructs the border from a random assortment of 8x8 tiles.
             * The border is six tiles thick: five random tiles, then the edges.
             * The shadows are on the right and bottom sides of the border.
             */

            borderTiles = new Dictionary<Vector2, string>();

            // Top: five tiles by the screen width over eight, left to right
            for (int y = 0; y < 40; y += 8)
            {
                int x;
                for (x = 0; x < screenSize.Y; x += 8)
                {
                    borderTiles.Add(new Vector2(x, y), GetRandomTileName());
                }
            }

            // Bottom: same size, but bottom from top instead
            for (int y = (int)screenSize.Y; y > screenSize.X - 48; y -= 8)
            {
                for (int x = 0; x < screenSize.Y; x += 8)
                {
                    borderTiles.Add(new Vector2(x, y), GetRandomTileName());
                }
            }

            // Left: same size, top to bottom, left to right
            for (int x = 0; x < 40; x += 8)
            {
                for (int y = 40; y < (screenSize.X - 40); y += 8)
                {
                    borderTiles.Add(new Vector2(x, y), GetRandomTileName());
                }
            }

            // Right: same as left
            for (int x = (int)screenSize.Y - 40; x < screenSize.Y; x += 8)
            {
                for (int y = 40; y < (screenSize.X - 40); y += 8)
                {
                    borderTiles.Add(new Vector2(x, y), GetRandomTileName());
                }
            }
            
            // Shadow corners
            borderTiles[new Vector2(screenSize.Y - 40, 48)] = "shadow_topright";
            borderTiles[new Vector2(screenSize.Y - 40, screenSize.X - 40)] = "shadow_bottomright";
            borderTiles[new Vector2(48, screenSize.X - 40)] = "shadow_bottomleft";

            // Shadow edge right
            for (int y = 48; y < (screenSize.X - 40); y += 8)
            {
                borderTiles[new Vector2(screenSize.Y - 40, y)] = "shadow_right";
            }

            // Shadow edge bottom
            for (int x = 48; x < screenSize.Y - 40; x += 8)
            {
                borderTiles[new Vector2(x, screenSize.X - 40)] = "shadow_bottom";
            }

            // Edge corners
            borderTiles[new Vector2(40, 40)] = "edge_topleft";
            borderTiles[new Vector2(screenSize.Y - 48, 40)] = "edge_topright";
            borderTiles[new Vector2(40, screenSize.X - 48)] = "edge_bottomleft";
            borderTiles[new Vector2(screenSize.Y - 48, screenSize.X - 48)] = "edge_bottomright";

            // Edge top and bottom
            for (int x = 48; x < screenSize.Y - 48; x += 8)
            {
                borderTiles[new Vector2(x, 40)] = "edge_top";
                borderTiles[new Vector2(x, screenSize.X - 48)] = "edge_bottom";
            }

            // Edge left and right
            for (int y = 48; y < screenSize.X - 48; y += 8)
            {
                borderTiles[new Vector2(40, y)] = "edge_left";
                borderTiles[new Vector2(screenSize.Y - 48, y)] = "edge_right";
            }
        }

        private void ConstructSMB3Border()
        {
            throw new NotImplementedException();
        }

        private string GetRandomTileName()
        {
            // 95% of the time, this tile will be the default tile.
            if (random.Next(0, 25) != 0)
            {
                return "tile";
            }
            else
            {
                return randomTileNames[random.Next(0, 3)];
            }
        }

        private void LoadFont(ContentManager Content)
        {
            Dictionary<char, Rectangle> charMap = new Dictionary<char, Rectangle>();
            charMap.Add('A', new Rectangle(0, 0, 8, 8));
            charMap.Add('B', new Rectangle(8, 0, 8, 8));
            charMap.Add('C', new Rectangle(16, 0, 8, 8));
            charMap.Add('D', new Rectangle(24, 0, 8, 8));
            charMap.Add('E', new Rectangle(32, 0, 8, 8));
            charMap.Add('F', new Rectangle(40, 0, 8, 8));
            charMap.Add('G', new Rectangle(48, 0, 8, 8));
            charMap.Add('H', new Rectangle(56, 0, 8, 8));
            charMap.Add('I', new Rectangle(64, 0, 8, 8));
            charMap.Add('J', new Rectangle(72, 0, 8, 8));
            charMap.Add('K', new Rectangle(80, 0, 8, 8));
            charMap.Add('L', new Rectangle(88, 0, 8, 8));
            charMap.Add('M', new Rectangle(96, 0, 8, 8));
            charMap.Add('N', new Rectangle(104, 0, 8, 8));
            charMap.Add('O', new Rectangle(112, 0, 8, 8));
            charMap.Add('P', new Rectangle(120, 0, 8, 8));
            charMap.Add('Q', new Rectangle(0, 8, 8, 8));
            charMap.Add('R', new Rectangle(8, 8, 8, 8));
            charMap.Add('S', new Rectangle(16, 8, 8, 8));
            charMap.Add('T', new Rectangle(24, 8, 8, 8));
            charMap.Add('U', new Rectangle(32, 8, 8, 8));
            charMap.Add('V', new Rectangle(40, 8, 8, 8));
            charMap.Add('W', new Rectangle(48, 8, 8, 8));
            charMap.Add('X', new Rectangle(56, 8, 8, 8));
            charMap.Add('Y', new Rectangle(64, 8, 8, 8));
            charMap.Add('Z', new Rectangle(72, 8, 8, 8));
            charMap.Add('!', new Rectangle(80, 8, 8, 8));
            charMap.Add('.', new Rectangle(88, 8, 8, 8));
            charMap.Add('-', new Rectangle(96, 8, 8, 8));
            charMap.Add(',', new Rectangle(104, 8, 8, 8));
            charMap.Add('?', new Rectangle(112, 8, 8, 8));
            charMap.Add(' ', new Rectangle(120, 8, 8, 8));
            charMap.Add('a', new Rectangle(0, 32, 8, 8));
            charMap.Add('b', new Rectangle(8, 32, 8, 8));
            charMap.Add('c', new Rectangle(16, 32, 8, 8));
            charMap.Add('d', new Rectangle(24, 32, 8, 8));
            charMap.Add('e', new Rectangle(32, 32, 8, 8));
            charMap.Add('f', new Rectangle(40, 32, 8, 8));
            charMap.Add('g', new Rectangle(48, 32, 8, 8));
            charMap.Add('h', new Rectangle(56, 32, 8, 8));
            charMap.Add('i', new Rectangle(64, 32, 8, 8));
            charMap.Add('j', new Rectangle(72, 32, 8, 8));
            charMap.Add('k', new Rectangle(80, 32, 8, 8));
            charMap.Add('l', new Rectangle(88, 32, 8, 8));
            charMap.Add('m', new Rectangle(96, 32, 8, 8));
            charMap.Add('n', new Rectangle(104, 32, 8, 8));
            charMap.Add('o', new Rectangle(112, 32, 8, 8));
            charMap.Add('p', new Rectangle(120, 32, 8, 8));
            charMap.Add('q', new Rectangle(0, 40, 8, 8));
            charMap.Add('r', new Rectangle(8, 40, 8, 8));
            charMap.Add('s', new Rectangle(16, 40, 8, 8));
            charMap.Add('t', new Rectangle(24, 40, 8, 8));
            charMap.Add('u', new Rectangle(32, 40, 8, 8));
            charMap.Add('v', new Rectangle(40, 40, 8, 8));
            charMap.Add('w', new Rectangle(48, 40, 8, 8));
            charMap.Add('x', new Rectangle(56, 40, 8, 8));
            charMap.Add('y', new Rectangle(64, 40, 8, 8));
            charMap.Add('z', new Rectangle(72, 40, 8, 8));
            charMap.Add('#', new Rectangle(80, 40, 8, 8));
            charMap.Add('(', new Rectangle(88, 40, 8, 8));
            charMap.Add(')', new Rectangle(96, 40, 8, 8));
            charMap.Add("'"[0], new Rectangle(104, 40, 8, 8));
            charMap.Add('1', new Rectangle(32, 48, 8, 8));
            charMap.Add('2', new Rectangle(40, 48, 8, 8));
            charMap.Add('3', new Rectangle(48, 48, 8, 8));
            charMap.Add('4', new Rectangle(56, 48, 8, 8));
            charMap.Add('5', new Rectangle(64, 48, 8, 8));
            charMap.Add('6', new Rectangle(72, 48, 8, 8));
            charMap.Add('7', new Rectangle(80, 48, 8, 8));
            charMap.Add('0', new Rectangle(88, 48, 8, 8)); // TODO: make numbers 8, 9

            font = new BitmapFontHandler(Content, @"world\border\font", charMap);
        }
        #endregion
    }

    public enum WorldBorderType
    {
        SuperMarioBros3,
        SuperMarioWorld
    }
}
